package cloud.ipanda.base;

import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;

/**
 * word 导出工具类
 */
public class WordUtils {

    /**
     * 用于判断导出的word是否转pdf
     */
    public static int judgment = 0;

    /**
     * 特殊符号集合
     */
    private static List<String> SPECIAL_SYMBOL_LIST=new ArrayList<>(3);
    static {
        SPECIAL_SYMBOL_LIST.add("$");
        SPECIAL_SYMBOL_LIST.add("{");
        SPECIAL_SYMBOL_LIST.add("}");
    }

    public WordUtils() {
        judgment = 0;
    }


    /**
     * 根据模板生成word
     *
     * @param path      模板的路径
     * @param params    需要替换的参数
     * @param tableList 需要插入的参数
     * @param fileName  生成word文件的文件名
     */
    public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName) throws Exception {
        String saveRoute = path.substring(0, path.indexOf("\\"));

        File file = new File(path);
        InputStream is = new FileInputStream(file);
        CustomXWPFDocument doc = new CustomXWPFDocument(is);

        this.replaceInPara(doc, params); // 替换文本里面的变量
        this.replaceInTable(doc, params, tableList); // 替换表格里面的变量
        OutputStream os = null;
        if (judgment == 0) {
            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
            os = new FileOutputStream(saveRoute + "\\word\\" + fileName);

            doc.write(os);

            String pdf = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; // 截掉
            //wToPdfChange(saveRoute + "\\word\\" + fileName, saveRoute + "\\word\\" + pdf);
        } else {
//			os = response.getOutputStream();
            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
            os = new FileOutputStream(saveRoute + "\\word\\" + fileName);
            //response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            doc.write(os);
            judgment = 0;
        }

        this.close(os);
        this.close(is);
    }
    /**
     * 根据模板生成word
     *
     * @param path      模板的路径
     * @param params    需要替换的参数
     * @param tableList 需要插入的参数
     * @param fileName  生成word文件的文件名
     * @param response
     */
    public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName,
                        HttpServletResponse response) throws Exception {
        String saveRoute = path.substring(0, path.indexOf("\\"));

        File file = new File(path);
        InputStream is = new FileInputStream(file);
        CustomXWPFDocument doc = new CustomXWPFDocument(is);

        this.replaceInPara(doc, params); // 替换文本里面的变量
        this.replaceInTable(doc, params, tableList); // 替换表格里面的变量
        OutputStream os = null;
        if (judgment == 0) {
            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
            os = new FileOutputStream(saveRoute + "\\word\\" + fileName);

            doc.write(os);

            String pdf = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; // 截掉
            wToPdfChange(saveRoute + "\\word\\" + fileName, saveRoute + "\\word\\" + pdf);

            try {
                System.out.println("========================pdf下载开始========================");
                // path是指欲下载的文件的路径。
                file = new File(saveRoute + "\\word\\" + pdf);
                // 取得文件名。
                String filename = URLEncoder.encode(file.getName(), "utf-8");
                // 取得文件的后缀名。
                String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
                // 以流的形式下载文件。
                is = new BufferedInputStream(new FileInputStream(saveRoute + "\\word\\" + pdf));
                byte[] buffer = new byte[is.available()];
                is.read(buffer);
                // 清空response
                response.reset();
                // 设置response的Header
                response.addHeader("Content-Disposition", "attachment;filename=" + filename);
                response.addHeader("Content-Length", "" + file.length());
                os = new BufferedOutputStream(response.getOutputStream());
                response.setContentType("application/octet-stream");
                os.write(buffer);
                os.flush();
                System.out.println("========================pdf下载结束========================");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } else {
//			os = response.getOutputStream();
            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
            os = new FileOutputStream(saveRoute + "\\word\\" + fileName);
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            doc.write(os);
            judgment = 0;
        }

        this.close(os);
        this.close(is);
    }

    /**
     * word 转 pdf
     *
     * @param wordFile word 的路径 word 的路径
     * @param pdfFile  pdf 的路径
     */
    public static void wToPdfChange(String wordFile, String pdfFile) {
        ActiveXComponent app = null;
        Dispatch document = null;
        System.out.println("========================开始转换========================");
        try {
            // 打开word
            System.out.println("开始打开word");
            app = new ActiveXComponent("Word.Application");

            // 获得word中所有打开的文档
            Dispatch documents = app.getProperty("Documents").toDispatch();
            System.out.println("打开文件: " + wordFile);
            // 打开文档
            document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
            // 如果文件存在的话，不会覆盖，会直接报错，所以我们需要判断文件是否存在
            File target = new File(pdfFile);
            if (target.exists()) {
                target.delete();
            }
            System.out.println("另存为: " + pdfFile);
            Dispatch.call(document, "SaveAs", pdfFile, 17);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("转换失败" + e.getMessage());
        } finally {
            // 关闭office
//			app.invoke("Quit", 0);

            if (document != null) {
                // 关闭文档
                Dispatch.call(document, "Close", false);
            }
            if (app != null) {
                app.invoke("Quit", 0);
            }

            // 获取系统类型
            String osName = System.getProperty("os.name");
            // 判断是系统类型
            if (osName.toLowerCase().startsWith("win")) {
                System.out.println(osName);
                // window系统
                String killCmd = "taskkill /f /im wps.exe";
                String killCmd1 = "taskkill /f /im wpscenter.exe";
                Process p;
                try {
                    p = Runtime.getRuntime().exec(killCmd);
                    p = Runtime.getRuntime().exec(killCmd1);
                    int runnngStatus = p.waitFor();
                    System.out.println("已杀" + runnngStatus);
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("转换失败" + e.getMessage());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("转换失败" + e.getMessage());
                }
            }
        }
        System.out.println("========================转换结束========================");
    }

    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) throws IOException, InvalidFormatException {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            this.replaceInPara(para, params, doc);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) throws IOException, InvalidFormatException {
        List<XWPFRun> runs;
        Matcher matcher;
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString().trim();

                System.out.println(runText);

                // 纯英文 ，不包含特殊符号
                if (isFullLetter(runText) && ObjectUtil.isNotNull(params.get(runText))){
                    para.removeRun(i+1);
                    this.replaceContent(run,params.get(runText));
                    para.removeRun(i-1);
                    // 追加内容
                    //this.appendContent(para,params.get(runText).toString());
                }

                // 包含所有特殊符号
                if (textHasSymbolStatus(runText)==3){
                    this.replaceContent(run,params.get(runText.replaceAll("[^a-zA-Z0-9]", "").toString()));
                }


            }


        }
    }

    /**
     * 为表格插入数据，行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    private static void insertTable(XWPFTable table, List<String[]> tableList) {
        // 创建行,根据需要插入的数据添加新行，不处理表头
        for (int i = 0; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }
        // 遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        int length = table.getRows().size();
        for (int i = 1; i < length - 1; i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                String s = tableList.get(i - 1)[j];
                cell.setText(s);
            }
        }
    }

    /**
     * 替换表格里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) throws IOException, InvalidFormatException {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
//			System.out.println("------------>" + table.getRows().size());
            if (table.getRows().size() > 1) {
                // 判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
                if (this.matcher(table.getText()).find()) {
                    rows = table.getRows();
                    for (XWPFTableRow row : rows) {
                        cells = row.getTableCells();
                        for (XWPFTableCell cell : cells) {
                            paras = cell.getParagraphs();
                            for (XWPFParagraph para : paras) {
                                this.replaceInPara(para, params, doc);
                            }
                        }
                    }
                } else {
                    insertTable(table, tableList); // 插入数据
                }
            }
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        System.out.println(str);
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 根据图片类型，取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * 将输入流中的数据写入字节数组
     *
     * @param in
     * @return
     */
    public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
        byte[] byteArray = null;
        try {
            int total = in.available();
            byteArray = new byte[total];
            in.read(byteArray);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isClose) {
                try {
                    in.close();
                } catch (Exception e2) {
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }

    /**
     * 关闭输入流
     *
     * @param is
     */
    public void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param os
     */
    public void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isFullLetter(String str){
        boolean result = str.matches("[a-zA-Z]+");
        return result;
    }


    /**
     * 统一添加内容 已有默认样式
     * @param para
     * @param content
     */
    public void appendContent(XWPFParagraph para,String content){
        this.appendContent(para,content,true,14,UnderlinePatterns.SINGLE);
    }

    public void appendContent(XWPFParagraph para,String content,boolean isBold,int fontSize,UnderlinePatterns underlinePatterns){
        XWPFRun xwpfRun=para.createRun();
        xwpfRun.setText(content);
        xwpfRun.setBold(true);
        xwpfRun.setFontSize(14);
        xwpfRun.setUnderline(underlinePatterns);
    }


    /**
     * 统一添加内容 已有默认样式
     * @param xwpfRun
     * @param content
     */
    public void replaceContent(XWPFRun xwpfRun,String content){
        this.replaceRunContent(xwpfRun,content,true,14,UnderlinePatterns.SINGLE);
    }

    public void replaceContent(XWPFRun xwpfRun,Object content) throws IOException, InvalidFormatException {
        this.replaceRunContent(xwpfRun,content,true,14,UnderlinePatterns.SINGLE);
    }


    public void replaceRunContent(XWPFRun xwpfRun,Object content,boolean isBold,int fontSize,UnderlinePatterns underlinePatterns) throws IOException, InvalidFormatException {
        if (content instanceof String){
            this.replaceRunContent(xwpfRun,content.toString(),isBold,fontSize,underlinePatterns);
        }else if (content instanceof List ){
            for (int i = 0; i < ((List<?>) content).size(); i++) {
                // 如果是文件
                if(((List<?>) content).get(i) instanceof File){
                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(FileUtil.readBytes((File) ((List<?>) content).get(i)));
                    //doc.addPictureData(byteInputStream, getPictureType("png"));
                    //sxwpfRun.addPicture(new FileInputStream(new File("d:\\1.png")),getPictureType("png"),"image.png", Units.toEMU(25),Units.toEMU(28));

                }
            }
        }
    }
    public void replaceRunContent(XWPFRun xwpfRun,String content,boolean isBold,int fontSize,UnderlinePatterns underlinePatterns){
        xwpfRun.setText(content,0);
        xwpfRun.setBold(true);
        xwpfRun.setFontSize(14);
        xwpfRun.setUnderline(underlinePatterns);
    }


    /**
     * 返回字符串包含特殊符号的个数
     * @param str
     * @return
     */
    private int textHasSymbolStatus(String str){

        AtomicInteger count= new AtomicInteger();
        SPECIAL_SYMBOL_LIST.forEach(symbol ->{
            if (str.indexOf(symbol)!=-1){
                count.getAndIncrement();
            }
        });
        return count.get();
    }
}